package com.sweet.json;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * jackson工具类
 *
 * @author limi
 * @create 2018-03-26-11:13
 */
@Slf4j
public final class JsonUtil {
    private JsonUtil() {
    }

    /**
     * 实例最大数量
     */
    private static final int INSTANCE_MAX_COUNT = 10;

    /**
     * ObjectMapper是綫程安全的，因此可以复用，但只允许初始化一次配置
     * 缺点：因为线程安全，则全局采用一个对象操作，影响性能
     * 改进：可建立对象池，便于分别执行操作
     * 测试：循环10次，每次1万序列化
     * 1. 每次创建，平均耗时：162毫秒
     * 2. 单利复用，平均耗时：36毫秒
     * 3. 多利复用，平均耗时：35毫秒
     */
    private static final List<ObjectMapper> INSTANCES = new ArrayList();

    static {
        for (int i = 0; i < INSTANCE_MAX_COUNT; i++) {
            //创建实例
            ObjectMapper mapper = new ObjectMapper();

            //属性值为null，则不序列化
            mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

            //属性未知，则反序列化时忽略，避免报错
            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

            //采用系统默认时区
            mapper.setTimeZone(TimeZone.getDefault());

            //格式化时间
            mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

            //长整型全部采用字符串形式
            //注意：工具类建议屏蔽，仅仅web项目向前端传递时，自带的json工具需要设置
            //SimpleModule simpleModule = new SimpleModule();
            //simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
            //simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
            //mapper.registerModule(simpleModule);

            INSTANCES.add(mapper);
        }
    }

    private static final ObjectMapper getMapper() {
        Random random = new Random();
        return INSTANCES.get(random.nextInt(INSTANCE_MAX_COUNT));
    }

    /**
     * 对象转为json字符串
     *
     * @param object
     * @return
     */
    public static String toJson(Object object) {
        if (object == null) {
            return null;
        }

        try {
            return getMapper().writeValueAsString(object);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            log.error("对象转为JSON字符串异常", e);
            return null;
        }
    }

    /**
     * json字符串转为对象
     *
     * @param json json字符串
     * @param cls
     * @param <T>
     * @return
     */
    public static <T> T toObject(String json, Class<T> cls) {
        if (isEmpty(json)) {
            return null;
        }

        try {
            return getMapper().readValue(json, cls);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("JSON字符串转为对象异常", e);
            return null;
        }
    }

    /**
     * map转为对象
     *
     * @param map
     * @param cls
     * @param <T>
     * @return
     */
    public static <T> T toObject(Map map, Class<T> cls) {
        if (map == null) {
            return null;
        }

        return getMapper().convertValue(map, cls);
    }

    /**
     * json字符串转为Collection
     *
     * @param json
     * @param cls
     * @return
     */
    public static <T> Collection<T> toCollection(String json, Class<T> cls) {
        if (isEmpty(json)) {
            return null;
        }

        try {
            ObjectMapper mapper = getMapper();
            return mapper.readValue(json, mapper.getTypeFactory().constructCollectionType(Collection.class, cls));
        } catch (IOException e) {
            e.printStackTrace();
            log.error("JSON字符串转为Collection异常", e);
            return null;
        }
    }

    /**
     * json字符串转为Set
     *
     * @param json
     * @param cls
     * @return
     */
    public static <T> Set<T> toSet(String json, Class<T> cls) {
        if (isEmpty(json)) {
            return null;
        }

        try {
            ObjectMapper mapper = getMapper();
            return mapper.readValue(json, mapper.getTypeFactory().constructCollectionType(Set.class, cls));
        } catch (IOException e) {
            e.printStackTrace();
            log.error("JSON字符串转为Set异常", e);
            return null;
        }
    }

    /**
     * json字符串转为List
     *
     * @param json
     * @param cls
     * @return
     */
    public static <T> List<T> toList(String json, Class<T> cls) {
        if (isEmpty(json)) {
            return null;
        }

        try {
            ObjectMapper mapper = getMapper();
            return mapper.readValue(json, mapper.getTypeFactory().constructCollectionType(List.class, cls));
        } catch (IOException e) {
            e.printStackTrace();
            log.error("JSON字符串转为List异常", e);
            return null;
        }
    }

    /**
     * json字符串转为map
     *
     * @param json
     * @param keyClass
     * @param valueClass
     * @return
     */
    public static <K, V> Map<K, V> toMap(String json, Class<? extends Map> mapClass, Class<K> keyClass,
                                         Class<V> valueClass) {
        if (isEmpty(json)) {
            return null;
        }

        try {
            ObjectMapper mapper = getMapper();
            return mapper.readValue(json, mapper.getTypeFactory().constructMapType(mapClass, keyClass, valueClass));
        } catch (IOException e) {
            e.printStackTrace();
            log.error("JSON字符串转为Map异常", e);
            return null;
        }
    }

    /**
     * bean转为Map
     * 注意：此处不是用filed直接定义map key，而是采用bean注解JsonProperty作为key
     *
     * @param object
     * @return
     */
    public static Map toMap(Object object) {
        if (object == null) {
            return null;
        }

        return getMapper().convertValue(object, Map.class);
    }

    /**
     * Map转为List
     *
     * @param maps
     * @param cls
     * @return
     */
    public static <T> List<T> toList(Collection<Map> maps, Class<T> cls) {
        if (maps == null) {
            return null;
        }

        if (maps.size() == 0) {
            return new ArrayList();
        }

        try {
            ObjectMapper mapper = getMapper();
            return mapper.convertValue(maps, mapper.getTypeFactory().constructCollectionType(List.class, cls));
        } catch (Exception e) {
            e.printStackTrace();
            log.error("Map转为List异常", e);
            return null;
        }
    }

    /**
     * 判断是否为空
     *
     * @param str
     * @return
     */
    private static boolean isEmpty(String str) {
        return str == null || str.length() == 0;
    }
}